#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
智能延迟策略模块

提供多种延迟策略，模拟真实用户行为，避免被检测为爬虫。
"""

import asyncio
import random
import logging
import time
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from enum import Enum

class DelayType(Enum):
    """延迟类型"""
    FIXED = "fixed"  # 固定延迟
    RANDOM = "random"  # 随机延迟
    EXPONENTIAL = "exponential"  # 指数退避
    ADAPTIVE = "adaptive"  # 自适应延迟
    HUMAN_LIKE = "human_like"  # 拟人化延迟

@dataclass
class RequestHistory:
    """请求历史记录"""
    timestamp: float
    success: bool
    response_time: float
    status_code: int = 0
    error: str = None

class DelayManager:
    """延迟管理器"""
    
    def __init__(self, config: Dict = None):
        self.logger = logging.getLogger(__name__)
        
        # 默认配置
        self.config = {
            "default_delay": 3.0,  # 默认延迟（秒）
            "min_delay": 1.0,      # 最小延迟
            "max_delay": 15.0,     # 最大延迟
            "delay_type": DelayType.ADAPTIVE,  # 默认延迟类型
            "success_rate_threshold": 0.8,    # 成功率阈值
            "failure_penalty": 2.0,           # 失败惩罚倍数
            "success_reward": 0.8,            # 成功奖励倍数
            "history_size": 100,              # 历史记录大小
            "work_hours": (9, 18),            # 工作时间段
            "night_penalty": 1.5,             # 夜间延迟倍数
            "weekend_penalty": 1.2,           # 周末延迟倍数
        }
        
        if config:
            self.config.update(config)
        
        # 请求历史
        self.request_history: List[RequestHistory] = []
        
        # 当前延迟设置
        self.current_delay = self.config["default_delay"]
        
        # 统计信息
        self.stats = {
            "total_requests": 0,
            "successful_requests": 0,
            "failed_requests": 0,
            "avg_delay": self.config["default_delay"],
            "last_success_rate": 1.0
        }
        
        self.logger.info("延迟管理器初始化完成")
    
    async def wait(self, delay_type: DelayType = None) -> float:
        """
        执行延迟等待
        
        Args:
            delay_type: 延迟类型，如不指定则使用配置的默认类型
            
        Returns:
            float: 实际延迟时间
        """
        if delay_type is None:
            delay_type = self.config["delay_type"]
        
        # 计算延迟时间
        delay_time = self._calculate_delay(delay_type)
        
        # 记录日志
        self.logger.debug(f"执行{delay_type.value}延迟: {delay_time:.2f}秒")
        
        # 执行延迟
        await asyncio.sleep(delay_time)
        
        return delay_time
    
    def _calculate_delay(self, delay_type: DelayType) -> float:
        """计算延迟时间"""
        base_delay = self.current_delay
        
        if delay_type == DelayType.FIXED:
            delay = base_delay
            
        elif delay_type == DelayType.RANDOM:
            min_delay = max(self.config["min_delay"], base_delay * 0.5)
            max_delay = min(self.config["max_delay"], base_delay * 2.0)
            delay = random.uniform(min_delay, max_delay)
            
        elif delay_type == DelayType.EXPONENTIAL:
            # 基于失败次数的指数退避
            recent_failures = self._get_recent_failure_count()
            delay = base_delay * (2 ** min(recent_failures, 5))  # 最大32倍
            
        elif delay_type == DelayType.ADAPTIVE:
            delay = self._adaptive_delay()
            
        elif delay_type == DelayType.HUMAN_LIKE:
            delay = self._human_like_delay()
            
        else:
            delay = base_delay
        
        # 应用时间因子
        delay = self._apply_time_factors(delay)
        
        # 确保在合理范围内
        delay = max(self.config["min_delay"], min(self.config["max_delay"], delay))
        
        return delay
    
    def _adaptive_delay(self) -> float:
        """自适应延迟策略"""
        if not self.request_history:
            return self.current_delay
        
        success_rate = self._calculate_success_rate()
        
        # 根据成功率调整延迟
        if success_rate >= self.config["success_rate_threshold"]:
            # 成功率高，可以减少延迟
            adjusted_delay = self.current_delay * self.config["success_reward"]
        else:
            # 成功率低，增加延迟
            penalty = 1 + (1 - success_rate) * self.config["failure_penalty"]
            adjusted_delay = self.current_delay * penalty
        
        # 更新当前延迟（渐进式调整）
        self.current_delay = (self.current_delay * 0.7 + adjusted_delay * 0.3)
        
        return self.current_delay
    
    def _human_like_delay(self) -> float:
        """拟人化延迟策略"""
        base_delay = self.current_delay
        
        # 模拟人类行为的随机性
        # 大部分时间是正常延迟，偶尔有长时间思考或短时间快速操作
        rand = random.random()
        
        if rand < 0.05:  # 5%概率长时间停顿（深度思考）
            delay = base_delay * random.uniform(3.0, 8.0)
        elif rand < 0.15:  # 10%概率短暂停顿
            delay = base_delay * random.uniform(0.3, 0.7)
        elif rand < 0.25:  # 10%概率快速连续操作
            delay = base_delay * random.uniform(0.1, 0.4)
        else:  # 70%概率正常延迟
            delay = base_delay * random.uniform(0.8, 1.5)
        
        # 添加微小的随机波动（模拟手部精确度差异）
        jitter = random.gauss(0, delay * 0.1)
        delay += jitter
        
        return max(0.1, delay)  # 最小0.1秒
    
    def _apply_time_factors(self, delay: float) -> float:
        """应用时间因子"""
        now = datetime.now()
        
        # 工作时间外的延迟惩罚
        if not (self.config["work_hours"][0] <= now.hour < self.config["work_hours"][1]):
            delay *= self.config["night_penalty"]
        
        # 周末延迟惩罚
        if now.weekday() >= 5:  # 周六、周日
            delay *= self.config["weekend_penalty"]
        
        # 午休时间（12-14点）略微增加延迟
        if 12 <= now.hour < 14:
            delay *= 1.1
        
        return delay
    
    def _calculate_success_rate(self, window_size: int = 20) -> float:
        """计算最近的成功率"""
        if not self.request_history:
            return 1.0
        
        recent_requests = self.request_history[-window_size:]
        successful = sum(1 for req in recent_requests if req.success)
        
        return successful / len(recent_requests)
    
    def _get_recent_failure_count(self, window_size: int = 5) -> int:
        """获取最近的连续失败次数"""
        if not self.request_history:
            return 0
        
        count = 0
        for req in reversed(self.request_history[-window_size:]):
            if not req.success:
                count += 1
            else:
                break
        
        return count
    
    def record_request(self, success: bool, response_time: float = 0.0, 
                      status_code: int = 200, error: str = None):
        """记录请求结果"""
        history = RequestHistory(
            timestamp=time.time(),
            success=success,
            response_time=response_time,
            status_code=status_code,
            error=error
        )
        
        self.request_history.append(history)
        
        # 限制历史记录大小
        if len(self.request_history) > self.config["history_size"]:
            self.request_history = self.request_history[-self.config["history_size"]:]
        
        # 更新统计信息
        self._update_stats()
        
        self.logger.debug(f"记录请求: 成功={success}, 响应时间={response_time:.2f}s")
    
    def _update_stats(self):
        """更新统计信息"""
        self.stats["total_requests"] = len(self.request_history)
        self.stats["successful_requests"] = sum(1 for req in self.request_history if req.success)
        self.stats["failed_requests"] = self.stats["total_requests"] - self.stats["successful_requests"]
        
        if self.stats["total_requests"] > 0:
            self.stats["last_success_rate"] = self.stats["successful_requests"] / self.stats["total_requests"]
        
        # 计算平均延迟（基于最近的调整）
        if self.request_history:
            recent_history = self.request_history[-10:]  # 最近10次请求
            total_time = sum(req.response_time for req in recent_history)
            self.stats["avg_delay"] = total_time / len(recent_history)
    
    def get_recommended_delay(self, url: str = None, operation: str = None) -> Tuple[DelayType, float]:
        """
        获取推荐的延迟策略和时间
        
        Args:
            url: 目标URL（用于特定网站的策略）
            operation: 操作类型（如'search', 'detail', 'comment'）
            
        Returns:
            Tuple[DelayType, float]: 推荐的延迟类型和预估时间
        """
        success_rate = self._calculate_success_rate()
        recent_failures = self._get_recent_failure_count()
        
        # 根据成功率选择策略
        if success_rate >= 0.9 and recent_failures == 0:
            # 高成功率，使用拟人化延迟
            delay_type = DelayType.HUMAN_LIKE
        elif success_rate >= 0.7:
            # 中等成功率，使用自适应延迟
            delay_type = DelayType.ADAPTIVE
        elif recent_failures >= 3:
            # 连续失败，使用指数退避
            delay_type = DelayType.EXPONENTIAL
        else:
            # 其他情况，使用随机延迟
            delay_type = DelayType.RANDOM
        
        estimated_delay = self._calculate_delay(delay_type)
        
        return delay_type, estimated_delay
    
    def get_delay_schedule(self, request_count: int) -> List[Tuple[int, float]]:
        """
        生成延迟调度计划
        
        Args:
            request_count: 预计请求数量
            
        Returns:
            List[Tuple[int, float]]: (请求序号, 延迟时间) 的列表
        """
        schedule = []
        
        for i in range(request_count):
            # 模拟不同阶段的延迟策略
            if i < 5:
                # 初始阶段，保守延迟
                delay = self.config["default_delay"] * random.uniform(1.2, 1.8)
            elif i < request_count * 0.3:
                # 探索阶段，随机延迟
                delay = self._calculate_delay(DelayType.RANDOM)
            else:
                # 稳定阶段，自适应延迟
                delay = self._calculate_delay(DelayType.ADAPTIVE)
            
            schedule.append((i, delay))
        
        return schedule
    
    def get_statistics(self) -> Dict:
        """获取延迟统计信息"""
        stats = self.stats.copy()
        
        # 添加额外信息
        stats.update({
            "current_delay": self.current_delay,
            "success_rate": self.stats["last_success_rate"],
            "recent_failures": self._get_recent_failure_count(),
            "avg_response_time": self._get_avg_response_time(),
            "delay_config": self.config
        })
        
        return stats
    
    def _get_avg_response_time(self) -> float:
        """获取平均响应时间"""
        if not self.request_history:
            return 0.0
        
        recent_history = self.request_history[-20:]  # 最近20次请求
        total_time = sum(req.response_time for req in recent_history)
        
        return total_time / len(recent_history)
    
    def reset_stats(self):
        """重置统计信息"""
        self.request_history.clear()
        self.current_delay = self.config["default_delay"]
        self.stats = {
            "total_requests": 0,
            "successful_requests": 0,
            "failed_requests": 0,
            "avg_delay": self.config["default_delay"],
            "last_success_rate": 1.0
        }
        
        self.logger.info("延迟统计信息已重置")
    
    def adjust_config(self, **kwargs):
        """动态调整配置"""
        old_config = self.config.copy()
        
        for key, value in kwargs.items():
            if key in self.config:
                self.config[key] = value
        
        # 重新调整当前延迟
        if "default_delay" in kwargs:
            self.current_delay = kwargs["default_delay"]
        
        self.logger.info(f"延迟配置已更新: {kwargs}")
    
    async def batch_wait(self, count: int, delay_type: DelayType = None) -> List[float]:
        """
        批量延迟等待
        
        Args:
            count: 延迟次数
            delay_type: 延迟类型
            
        Returns:
            List[float]: 实际延迟时间列表
        """
        delays = []
        
        for i in range(count):
            delay_time = await self.wait(delay_type)
            delays.append(delay_time)
            
            # 记录每次延迟作为成功的请求（用于统计）
            self.record_request(success=True, response_time=delay_time)
        
        return delays